package com.xnyzc.lhy.common.component.payment;

import com.alibaba.fastjson.JSONObject;
import com.xnyzc.lhy.common.constant.PayConstant;
import com.xnyzc.lhy.common.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * @Description 微信支付工具类
 * @Author 刘举
 * @Date 2019/8/7 11:19
 **/
@Slf4j
@Component
public class WeChatPayUtil {

    private static String charset = "UTF-8";

    private static String wxAppId;

    private static String wxPartnerId;

    private static String wxAppKey;

    @Value("${global.weChat.pay.appId}")
    public void setAppId(String appId) {
        wxAppId = appId;
    }

    @Value("${global.weChat.pay.partnerId}")
    public void setPartnerId(String partnerId) {
        wxPartnerId = partnerId;
    }

    @Value("${global.weChat.pay.appKey}")
    public void setAppKey(String appKey) {
        wxAppKey = appKey;
    }

    /***
     * 获取预支付交易单
     * @return
     */
    public static String getWXPrepayID(Map<String, Object> map) {
        String prepayId = null;
        String xml = mapToXML(map);

        try {
            URL url = new URL(PayConstant.WX_PAY_URL);
            HttpURLConnection http = (HttpURLConnection) url.openConnection();
            http.setRequestMethod("POST");
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            http.connect();
            OutputStream os = http.getOutputStream();
            os.write(xml.getBytes(charset));
            os.flush();
            os.close();
            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes, charset);
            Document doc = DocumentHelper.parseText(message);
            Element rootElement = doc.getRootElement();
            List<Element> elements = rootElement.elements();
            for (int i = 0; i < elements.size(); i++) {
                Element element = elements.get(i);
                String elementName = element.getName();
                if ("prepay_id".equals(elementName)) {
                    prepayId = element.getText();
                }
            }
        } catch (MalformedURLException e) {
            log.error("获取预支付交易单异常");
        } catch (IOException e) {
            log.error("获取预支付交易单异常");
        } catch (DocumentException e) {
            log.error("获取预支付交易单异常");
        }
        return prepayId;
    }

    /**
     * 统一下单 组装参数
     *
     * @param orderNo
     * @param orderSubject
     * @param totalPrice
     * @param notifyUrl
     * @param passBackJson
     * @return
     */
    public static Map<String, Object> requestParam(String orderNo, String orderSubject, String totalPrice, String notifyUrl, JSONObject passBackJson) {

        // 日期往后推一天的结果
        Date date = new Date();
        String startTime = new SimpleDateFormat("yyyyMMddHHmmss").format(date);
        String endTime = new SimpleDateFormat("yyyyMMddHHmmss").format(DateUtil.dateAddDays(date, 1));

        Map<String, Object> map = new HashMap<>(16);
        map.put("appid", wxAppId);
        // 附加数据
        passBackJson.put("orderNo", orderNo);
        map.put("attach", passBackJson.toJSONString());
        map.put("body", orderSubject);
        map.put("detail", orderSubject);
        map.put("fee_type", "CNY");
        map.put("goods_tag", "WXG");
        map.put("limit_pay", "no_credit");
        map.put("mch_id", wxPartnerId);
        map.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", ""));
        map.put("notify_url", notifyUrl);
        map.put("out_trade_no", orderNo + "_" + getNonceStr());
        map.put("product_id", orderNo);
        try {
            map.put("spbill_create_ip", InetAddress.getLocalHost().getHostAddress());
        } catch (UnknownHostException e) {
            log.error("微信支付，获取终端IP失败，订单号：{}", orderNo);
            // 设置默认IP
            map.put("spbill_create_ip", "47.93.14.145");
        }
        map.put("time_start", startTime);
        map.put("time_expire", endTime);
        map.put("total_fee", totalPrice);
        map.put("trade_type", "APP");
        map.put("sign", getSign(map));
        return map;
    }

    /***
     * 获取签名
     * @param map
     * @return
     */
    public static String getSign(Map<String, Object> map) {
        StringBuffer sb = new StringBuffer();
        sb.append("appid=" + map.get("appid"));
        sb.append("&attach=" + map.get("attach"));
        sb.append("&body=" + map.get("body"));
        sb.append("&detail=" + map.get("detail"));
        sb.append("&fee_type=" + map.get("fee_type"));
        sb.append("&goods_tag=" + map.get("goods_tag"));
        sb.append("&limit_pay=" + map.get("limit_pay"));
        sb.append("&mch_id=" + map.get("mch_id"));
        sb.append("&nonce_str=" + map.get("nonce_str"));
        sb.append("&notify_url=" + map.get("notify_url"));
        sb.append("&out_trade_no=" + map.get("out_trade_no"));
        sb.append("&product_id=" + map.get("product_id"));
        sb.append("&spbill_create_ip=" + map.get("spbill_create_ip"));
        sb.append("&time_expire=" + map.get("time_expire"));
        sb.append("&time_start=" + map.get("time_start"));
        sb.append("&total_fee=" + map.get("total_fee"));
        sb.append("&trade_type=" + map.get("trade_type"));
        String a = sb.toString();
        String stringSignTemp = a + "&key=" + wxAppKey;
        String sign = MD5Encode(stringSignTemp, "UTF-8").toUpperCase();
        return sign;
    }

    public static String mapToXML(Map<String, Object> map) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        sb.append("<appid>" + map.get("appid") + "</appid>");
        sb.append("<attach>" + map.get("attach") + "</attach>");
        sb.append("<body>" + map.get("body") + "</body>");
        sb.append("<detail>" + map.get("detail") + "</detail>");
        sb.append("<fee_type>" + map.get("fee_type") + "</fee_type>");
        sb.append("<goods_tag>" + map.get("goods_tag") + "</goods_tag>");
        sb.append("<limit_pay>" + map.get("limit_pay") + "</limit_pay>");
        sb.append("<mch_id>" + map.get("mch_id") + "</mch_id>");
        sb.append("<nonce_str>" + map.get("nonce_str") + "</nonce_str>");
        sb.append("<notify_url>" + map.get("notify_url") + "</notify_url>");
        sb.append("<out_trade_no>" + map.get("out_trade_no") + "</out_trade_no>");
        sb.append("<product_id>" + map.get("product_id") + "</product_id>");
        sb.append("<spbill_create_ip>" + map.get("spbill_create_ip") + "</spbill_create_ip>");
        sb.append("<time_expire>" + map.get("time_expire") + "</time_expire>");
        sb.append("<time_start>" + map.get("time_start") + "</time_start>");
        sb.append("<total_fee>" + map.get("total_fee") + "</total_fee>");
        sb.append("<trade_type>" + map.get("trade_type") + "</trade_type>");
        sb.append("<sign>" + map.get("sign") + "</sign>");
        sb.append("</xml>");
        return sb.toString();
    }


    public static String getPaySign(Map<String, String> map) {
        StringBuffer sb = new StringBuffer();
        sb.append("appid=" + map.get("appid"));
        sb.append("&noncestr=" + map.get("noncestr"));
        sb.append("&package=" + map.get("package"));
        sb.append("&partnerid=" + map.get("partnerid"));
        sb.append("&prepayid=" + map.get("prepayid"));
        sb.append("&timestamp=" + map.get("timestamp"));
        String a = sb.toString();
        String stringSignTemp = a + "&key=" + wxAppKey;
        String sign = MD5Encode(stringSignTemp, "UTF-8").toUpperCase();
        return sign;
    }


    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            resultSb.append(byteToHexString(b[i]));
        }

        return resultSb.toString();
    }

    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n += 256;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    public static String MD5Encode(String origin, String charsetname) {
        String resultString = null;
        try {
            resultString =origin;
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname)) {
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes()));
            } else {
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes(charsetname)));
            }
        } catch (Exception exception) {
        }
        return resultString;
    }

    private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

    private static String getNonceStr() {
        Long nonce = Math.round(Math.random() * 8999 + 1000);
        return nonce.toString();
    }
}
